package com.wang.common.file.service;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.wang.common.file.config.OSSProperties;
import com.wang.common.file.domain.OSSResult;
import com.wang.common.core.constants.CacheConstants;
import com.wang.common.core.constants.Constant;
import com.wang.common.core.enums.ResultCode;
import com.wang.common.core.utils.ThreadLocalUtil;
import com.wang.common.redis.service.RedisService;
import com.wang.common.security.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RefreshScope
public class OSSService {

    @Autowired
    private OSSProperties prop;

    @Autowired
    private OSS ossClient;

    @Autowired
    private RedisService redisService;

    @Value("${file.max-time}")
    private int maxTime;

    @Value("${file.test}")
    private boolean test;

    public OSSResult uploadFile(MultipartFile file) throws Exception {
        //是否开启上传次数检查
        if (!test) {
            checkUploadCount();
        }
        InputStream inputStream = null;//数据输入流
        try {
            String fileName;
            //获取到文件的名字
            if (file.getOriginalFilename() != null) {
                fileName = file.getOriginalFilename().toLowerCase();
            } else {
                //如果没哟名字,就付一个默认的名字
                fileName = "a.png";
            }
            //截取该文件的扩展名
            String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
            inputStream = file.getInputStream();
            //上传文件
            return upload(extName, inputStream);
        } catch (Exception e) {
            log.error("OSS upload file error", e);
            throw new ServiceException(ResultCode.FAILED_FILE_UPLOAD);
        } finally {
            //最后一定要手动关闭输入流
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    private void checkUploadCount() {
        //获取到线程中的userId
        Long userId = ThreadLocalUtil.get(Constant.USER_ID, Long.class);
        //从缓存中获取到当前用户的上传次数
        Long times = redisService.getCacheMapValue(CacheConstants.USER_UPLOAD_TIMES_KEY, String.valueOf(userId), Long.class);
        //判断是否大于最大的限制次数
        if (times != null && times >= maxTime) {
            throw new ServiceException(ResultCode.FAILED_FILE_UPLOAD_TIME_LIMIT);
        }
        //给redis中用户的上传次数+1
        redisService.incrementHashValue(CacheConstants.USER_UPLOAD_TIMES_KEY, String.valueOf(userId), 1);
        //每日1点清空记录
        if (times == null || times == 0) {
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(CacheConstants.USER_UPLOAD_TIMES_KEY, seconds, TimeUnit.SECONDS);
        }
    }

    private OSSResult upload(String fileType, InputStream inputStream) {
        //拼接文件上传路径与文件
        //ObjectId.next()与雪花算法类似,时间戳（4字节） + 每一台机器生成的唯一随机值（5字节） + 同一秒内生成的唯一的计数器（3字节）
        String key = prop.getPathPrefix() + ObjectId.next() + "." + fileType;
        //设置文件的元数据
        ObjectMetadata objectMetadata = new ObjectMetadata();
        //设置文件的访问权限,PublicRead就是只有所有者可以修改,其他人只读
//        objectMetadata.setObjectAcl(CannedAccessControlList.PublicRead);
        //构建文件上传请求
        PutObjectRequest request = new PutObjectRequest(prop.getBucketName(), key, inputStream, objectMetadata);
        PutObjectResult putObjectResult;
        try {
            //上传文件,把之前构造的上传文件的请求丢进去
            putObjectResult = ossClient.putObject(request);
        } catch (Exception e) {
            log.error("OSS put object error: {}", ExceptionUtil.stacktraceToOneLineString(e, 500));
            throw new ServiceException(ResultCode.FAILED_FILE_UPLOAD);
        }
        //判断文件上传状态,如果上传失败,则返回false,如果成功返回文件名和true
        return assembleOSSResult(key, putObjectResult);
    }

    private OSSResult assembleOSSResult(String key, PutObjectResult putObjectResult) {
        OSSResult ossResult = new OSSResult();
        if (putObjectResult == null || StrUtil.isBlank(putObjectResult.getRequestId())) {
            ossResult.setSuccess(false);
        } else {
            ossResult.setSuccess(true);
            ossResult.setName(FileUtil.getName(key));
        }
        return ossResult;
    }
}
